﻿namespace CP.General
{
    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using DWORD = System.UInt32;
    using LPWSTR = System.String;
    using NET_API_STATUS = System.UInt32;

    /// <summary>
    /// Connects to a UNC (Universal Naming Convention) path with credentials
    /// refer to http://www.codeproject.com/Articles/43091/Connect-to-a-UNC-Path-with-Credentials
    /// </summary>
    public class UNCAccessor : IDisposable
    {
        private bool disposed = false;

        private string sUNCPath;
        private string sUser;
        private string sPassword;
        private string sDomain;
        private int iLastError;

        /// <summary>
        /// A disposeable class that allows access to a UNC resource with credentials.
        /// </summary>
        public UNCAccessor()
        {
        }

        /// <summary>
        /// The last system error code returned from NetUseAdd or NetUseDel.  Success = 0
        /// </summary>
        public int LastError
        {
            get { return iLastError; }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if(!this.disposed)
            {
                if (disposing)
                {
                    NetUseDelete();
                }
            }

            this.disposed = true;
        }

        /// <summary>
        /// Connects to a UNC path using the credentials supplied.
        /// </summary>
        /// <param name="uncPath">Fully qualified domain name UNC path</param>
        /// <param name="user">A user with sufficient rights to access the path.</param>
        /// <param name="domain">Domain of User.</param>
        /// <param name="password">Password of User</param>
        /// <returns>True if mapping succeeds.  Use LastError to get the system error code.</returns>
        public bool NetUseWithCredentials(string uncPath, string user, string domain, string password)
        {
            sUNCPath = uncPath;
            sUser = user;
            sPassword = password;
            sDomain = domain;
            return NetUseWithCredentials();
        }

        /// <summary>
        /// Ends the connection to the remote resource 
        /// </summary>
        /// <returns>True if it succeeds.  Use LastError to get the system error code</returns>
        public bool NetUseDelete()
        {
            uint returncode;
            try
            {
                returncode = NetUseDel(null, sUNCPath, 2);
                iLastError = (int)returncode;
                return returncode == 0;
            }
            catch
            {
                iLastError = Marshal.GetLastWin32Error();
                return false;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass"), DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern NET_API_STATUS NetUseAdd(LPWSTR uncServerName, DWORD level, ref USE_INFO_2 buf, out DWORD parmError);

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass"), DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern NET_API_STATUS NetUseDel(LPWSTR uncServerName, LPWSTR useName, DWORD forceCond);

        private bool NetUseWithCredentials()
        {
            if (string.IsNullOrEmpty(sUser) && string.IsNullOrEmpty(sPassword))
            {
                // in non-Credentials way
                return Directory.Exists(sUNCPath);
            }

            uint returncode;
            try
            {
                USE_INFO_2 useinfo = new USE_INFO_2();

                useinfo.Ui2_remote = sUNCPath;
                useinfo.Ui2_username = sUser;
                useinfo.Ui2_domainname = sDomain;
                useinfo.Ui2_password = sPassword;
                useinfo.Ui2_asg_type = 0;
                useinfo.Ui2_usecount = 1;
                uint paramErrorIndex;
                returncode = NetUseAdd(null, 2, ref useinfo, out paramErrorIndex);
                iLastError = (int)returncode;
                return returncode == 0;
            }
            catch
            {
                iLastError = Marshal.GetLastWin32Error();
                return false;
            }
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct USE_INFO_2
        {
            internal LPWSTR Ui2_local;
            internal LPWSTR Ui2_remote;
            internal LPWSTR Ui2_password;
            internal DWORD Ui2_status;
            internal DWORD Ui2_asg_type;
            internal DWORD Ui2_refcount;
            internal DWORD Ui2_usecount;
            internal LPWSTR Ui2_username;
            internal LPWSTR Ui2_domainname;
        }
    }
}
